<?php

Raise::load('core.datetime.RaiseDateTime');

/**
 * TestCoreRaiseDateTime
 * Testing unit of 'phpraise/core/RaiseDateTime.php'
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.UnitTest.Test
 */

class TestCoreRaiseDateTime extends RaiseTest {
    
    public static function testClass(){
        self::assertClassExists('RaiseDateTime');
        self::assertClassExtends('RaiseDateTime', 'RaiseDataType');
        self::assertClassImplements('RaiseDateTime', 'IRaiseComparable');
        self::assertClassMethod('RaiseDateTime', 
                array(
                    'add',
                    'subtract',
                    'compareTo',
                    'now',
                    'dayOfWeek',
                    'microtime',
                    'fromString',
                    'fromTimestamp',
                    'toTimestamp',
                    'convertTimezone',
                    'toFormat',
                    'toISO8601',
                    'toRFC822',
                    'isLeapYear',
                    'daysInMonth',
                    'calculateAge',
                    'date',
                    'time',
                    'timezone'
                ));
        self::assertConstantDefined(array(
            'RaiseDateTime::MONDAY',
            'RaiseDateTime::TUESDAY',
            'RaiseDateTime::WEDNESDAY',
            'RaiseDateTime::THURSDAY',
            'RaiseDateTime::FRIDAY',
            'RaiseDateTime::SATURDAY',
            'RaiseDateTime::SUNDAY',
            'RaiseDateTime::JANUARY',
            'RaiseDateTime::FEBRUARY',
            'RaiseDateTime::MARCH',
            'RaiseDateTime::APRIL',
            'RaiseDateTime::MAY',
            'RaiseDateTime::JUNE',
            'RaiseDateTime::JULY',
            'RaiseDateTime::AUGUST',
            'RaiseDateTime::SEPTEMBER',
            'RaiseDateTime::OCTOBER',
            'RaiseDateTime::NOVEMBER',
            'RaiseDateTime::DECEMBER'
            ));
    }
    
    public static function testCreate(){
        $dt = new RaiseDateTime(1, 1, 1);
        self::assertStrictEqual($dt->date()->year(), 1);
        self::assertStrictEqual($dt->date()->month(), 1);
        self::assertStrictEqual($dt->date()->day(), 1);
        self::assertStrictEqual($dt->time()->hour(), 0);
        self::assertStrictEqual($dt->time()->minute(), 0);
        self::assertStrictEqual($dt->time()->second(), 0);
        self::assertStrictEqual($dt->time()->millisecond(), 0);
        self::assertStrictEqual($dt->timezone(), 0);
    }
    
    public static function testNow(){
        $dt = RaiseDateTime::now();
        self::assertObjectClass($dt, 'RaiseDateTime');
        self::assertStrictEqual($dt->toTimestamp(), time());  
        self::assertStrictEqual($dt->date()->day(), (int)gmdate('j'));
    }
    
    public static function testDayOfWeek(){
        $dt = RaiseDateTime::now();
        self::assertType($dt->dayOfWeek(), 'integer');
        $dow = (int)gmdate('w');
        switch($dt->dayOfWeek()){
            case RaiseDateTime::SATURDAY:
                self::assertStrictEqual($dow, 6);
                break;
            case RaiseDateTime::SUNDAY:
                self::assertStrictEqual($dow, 0);
                break;
            case RaiseDateTime::MONDAY:
                self::assertStrictEqual($dow, 1);
                break;
            case RaiseDateTime::TUESDAY:
                self::assertStrictEqual($dow, 2);
                break;
            case RaiseDateTime::WEDNESDAY:
                self::assertStrictEqual($dow, 3);
                break;
            case RaiseDateTime::THURSDAY:
                self::assertStrictEqual($dow, 4);
                break;
            case RaiseDateTime::FRIDAY:
                self::assertStrictEqual($dow, 5);
                break;
        }
    }
    
    public static function testFromTimestamp(){
        $dt = RaiseDateTime::fromTimestamp(time());
        self::assertObjectClass($dt, 'RaiseDateTime');
        self::assertStrictEqual($dt->toTimestamp(), time());  
        self::assertStrictEqual($dt->date()->day(), (int)gmdate('j'));
        $dt2 = RaiseDateTime::fromTimestamp(time() - 86400);
        self::assertStrictEqual($dt2->toTimestamp(), time() - 86400);  
    }
    
    public static function testToFormat(){
        $dt = RaiseDateTime::now();
        self::assertType($dt->toFormat('jS'), 'string');
        self::assertStrictEqual($dt->toFormat('jS'), gmdate('jS'));
        self::assertStrictEqual($dt->toFormat('M'), gmdate('M'));
    }
    
    public static function testToTimestamp(){
        $dt = RaiseDateTime::now();
        self::assertStrictEqual($dt->toTimestamp(), time());
        $dt2 = new RaiseDateTime(1970, 1, 1);
        self::assertStrictEqual($dt2->toTimestamp(), 0);
    }
    
    public static function testCompare(){
        $dt = RaiseDateTime::now();
        $dt2 = RaiseDateTime::fromTimestamp(time() - 3600); // 1 hour before
        self::assertStrictEqual($dt->compareTo($dt2), -1);
        self::assertStrictEqual($dt2->compareTo($dt), 1);
    }
    
    public static function testFromString(){
        $dt = RaiseDateTime::fromString('10 September 2000');
        self::assertStrictEqual($dt->date()->day(), 10);
        self::assertStrictEqual($dt->date()->year(), 2000);
        self::assertStrictEqual($dt->toTimestamp(), strtotime('10 September 2000'));
    }

    public static function testDaysInMonth(){
        for($i = 1; $i < 13; $i++){
            self::assertType(RaiseDateTime::daysInMonth($i), 'integer');
        }
        try{
            RaiseDateTime::daysInMonth(13);
            self::addError('Failed to run RaiseDateTime::daysInMonth(13) properly.');
        }catch(Exception $e){
            self::assert($e);
        }
        self::assertStrictEqual(RaiseDateTime::daysInMonth(1), 31);
        self::assertStrictEqual(RaiseDateTime::daysInMonth(12), 31);
        self::assertStrictEqual(RaiseDateTime::daysInMonth(2, 2000), 29);
        self::assertStrictEqual(RaiseDateTime::daysInMonth(2, 2001), 28);
    }

    public static function testGetMicrotime(){
        $dt = RaiseDateTime::microtime();
        self::assertType($dt, 'double');
        self::assertTrue($dt > 0);
    }

    public static function testIsLeapYear(){
        self::assertTrue(RaiseDateTime::isLeapYear(2000));
        self::assertTrue(RaiseDateTime::isLeapYear(2004));
        self::assertTrue(RaiseDateTime::isLeapYear(1996));
        self::assertTrue(RaiseDateTime::isLeapYear(1992));
        self::assertTrue(RaiseDateTime::isLeapYear(2008));
        self::assertTrue(RaiseDateTime::isLeapYear(2012));
        self::assertFalse(RaiseDateTime::isLeapYear(2001));
        self::assertFalse(RaiseDateTime::isLeapYear(2005));
        self::assertFalse(RaiseDateTime::isLeapYear(1993));
        self::assertFalse(RaiseDateTime::isLeapYear(1990));
        self::assertFalse(RaiseDateTime::isLeapYear(2011));
        self::assertFalse(RaiseDateTime::isLeapYear(2010));
    }

    public static function testCalcAge(){
        $age = RaiseDateTime::calculateAge(RaiseDateTime::fromString('1992-1-1 00:00:00'));
        self::assertType($age, 'integer');
        self::assertStrictEqual($age, RaiseDateTime::now()->date()->year() - 1992);
    }
    
    public static function testAdd(){
        $dt = RaiseDateTime::now();
        $ts = new RaiseTimeSpan();
        $ts->day(2);
        $ts->hour(5);
        self::doTestDateAdd($dt, $ts);
    }
    
    public static function testAdd1(){
        $dt = RaiseDateTime::fromString('2011-12-13 22:00:00');
        $ts = new RaiseTimeSpan();
        $ts->day(2);
        $ts->hour(5);
        self::doTestDateAdd($dt, $ts);
    }
    
    public static function testAdd2(){
        $dt = RaiseDateTime::fromString('2011-12-13 23:00:00');
        $ts = new RaiseTimeSpan();
        $ts->day(1);
        $ts->hour(20);
        $ts->minute(55);
        self::doTestDateAdd($dt, $ts);
    }
    
    public static function testAdd3(){
        $dt = RaiseDateTime::fromString('2011-12-13 00:00:00');
        $ts = new RaiseTimeSpan();
        $ts->day(3);
        $ts->hour(20);
        self::doTestDateAdd($dt, $ts);
    }
    
    protected static function doTestDateAdd($dt, $ts){
        $dt2 = $dt->add($ts);
        self::assertObjectClass($dt2, 'RaiseDateTime');
        self::assertStrictEqual($dt2->toTimestamp() - (int)$ts->totalSeconds(), $dt->toTimestamp());
    }

    public static function testSubtract(){
        $dt = RaiseDateTime::now();
        $ts = new RaiseTimeSpan();
        $ts->day(2);
        $ts->hour(5);
        self::doTestDateSubtract($dt, $ts);
    }

    public static function testSubtract1(){
        $dt = RaiseDateTime::now();
        $ts = new RaiseTimeSpan(5);
        self::doTestDateSubtract($dt, $ts);
    }

    public static function testSubtract2(){
        $dt = RaiseDateTime::fromString('2011-12-13 05:00:00');
        $ts = new RaiseTimeSpan();
        $ts->day(2);
        $ts->hour(5);
        self::doTestDateSubtract($dt, $ts);
    }

    public static function testSubtract3(){
        $dt = RaiseDateTime::fromString('2011-12-13 04:00:00');
        $ts = new RaiseTimeSpan();
        $ts->day(2);
        $ts->hour(5);
        self::doTestDateSubtract($dt, $ts);
    }
    
    protected static function doTestDateSubtract($dt, $ts){
        $dt2 = $dt->subtract($ts);
        self::assertObjectClass($dt2, 'RaiseDateTime');
        self::assertStrictEqual($dt2->toTimestamp(), $dt->toTimestamp() - (int)$ts->totalSeconds());
    }
    
    public static function testConvertTimezone(){
        $dt = RaiseDateTime::now();
        $dt2 = RaiseDateTime::convertTimezone($dt, '8');
        self::assertStrictEqual($dt->toTimestamp(), $dt2->toTimestamp());
        self::assertNotEqual($dt->time()->hour(), $dt2->time()->hour());
        self::assertStrictEqual($dt->timezone(), 0);
        self::assertStrictEqual($dt2->timezone(), 8);
        self::assertStrictEqual($dt2->time()->subtract(new RaiseTime(8))->totalSeconds(), $dt->time()->totalSeconds());
        $dt2->timezone(6);
        self::assertStrictEqual($dt2->timezone(), 6);
        self::assertStrictEqual($dt->time()->add(new RaiseTime(6))->totalSeconds(), $dt2->time()->totalSeconds());
    }
    
}